// project created on 11/4/2006 at 5:02 PM

// ===================================== TODOs =====================================
//	*	When grabbing from YourTV, work out prorgamme type (for sports or series only)

using System;
using System.Net;
//using System.Collections;
using System.Collections.Generic;
using System.IO;

namespace tvgrabau
{
	public static class YourTV
	{
		public static DateTime dt;
		public static CookieContainer CookieContainer = new CookieContainer();

		private static List<Programme> SpitzeListe = new List<Programme>();
		
		public static string Label = "Fetching listings...";
		public static int Width = 40;
		public static double Percent = 0;
		
		public static string CFID = "";

		public static void Fetch()
		{		    
			WebActions.Mirrors = tvgrabau.MainClass.options.RemoteHosts;
			//Console.WriteLine("Remote hosts: {0}", tvgrabau.MainClass.options.RemoteHosts.Length);
		
			WebActions.PickRandomMirror();
			WebActions.CheckUpdates();
		
			// Do the inital setup..
			Setup.SetRegion(tvgrabau.MainClass.options.Region.FreeToAir, tvgrabau.MainClass.options.Region.Cable, tvgrabau.MainClass.options.VenueIDList);
			
			// Go start grabbing!
			if (MainClass.Width != 0)
			{
				Width = MainClass.Width;
			}
			
			//Console.WriteLine("Width: {0}", Width);
			PerformGrab();		
			//MainClass.ReturnCode = 0;
		}
		
		public static void Configure()
		{
			// Ask for cache settings.
			Console.Write("Would you like to cache data locally? [yes] ");
			string clocal = Console.ReadLine().ToLower(); 
			if (clocal == "y" || clocal == "yes" || clocal.Trim().Length == 0)
			{
			    string TVGADir = Environment.GetFolderPath(System.Environment.SpecialFolder.Personal) + System.IO.Path.DirectorySeparatorChar + ".tvgrabau" + System.IO.Path.DirectorySeparatorChar + "cache";
				MainClass.ConfigureOptions.CacheEnabled = true;
				Console.Write("Where would you like to store the cache? [{0}] ", TVGADir);
				string clocation = Console.ReadLine();
				if (clocation.Trim().Length == 0)
				{
					MainClass.ConfigureOptions.CacheLocation = TVGADir;
				}
				else
				{
					MainClass.ConfigureOptions.CacheLocation = clocation;
				}
			}
			
			Console.Write("Do you need to use a proxy? [no] ");
			string proxy = Console.ReadLine().ToLower();
			if (proxy.StartsWith("y"))
			{
			    Console.Write("Proxy hostname/IP: ");
			    MainClass.ConfigureOptions.Proxy.Hostname = Console.ReadLine();
			    
			    Console.Write("Proxy port: ");
			    MainClass.ConfigureOptions.Proxy.Port = int.Parse(Console.ReadLine());
			    
			    Console.Write("Username (leave blank for none): ");
			    string usr = Console.ReadLine().Trim();
			    if (usr.Length > 0)
			    {
			        MainClass.ConfigureOptions.Proxy.Username = usr;
			        Console.Write("Password: ");
			        string passy = Console.ReadLine();
			        MainClass.ConfigureOptions.Proxy.Password = passy;
			    }
			    
			    MainClass.ConfigureOptions.Proxy.Enabled = true;
			}
			
			// Bootstrap for mirrors.
			Console.Write("Please enter a mirror list to bootstrap from: [http://mediati.org/tvgrabau.mirrors] ");
			string bootstrapMirror = Console.ReadLine();
			string listSource = "";
			if (bootstrapMirror.Length == 0)
			{
				bootstrapMirror = "http://mediati.org/tvgrabau.mirrors";
			}

            // Set the config variable so we can use proxy if necessary.
            MainClass.options = MainClass.ConfigureOptions;
			
			// Grab the list, and parse it.
			Console.Write("Downloading mirror list... ");
			MainClass.loud = false;
			listSource = WebManager.GetPage(bootstrapMirror, false);
			if (listSource != "")
			{
			    Console.WriteLine("OK");
			}
			else
			{
			    Console.WriteLine("FAIL");
			    Console.WriteLine("Houston, we have a problem. We couldn't fetch the mirror list provided.");
			    Console.WriteLine("Either wait a while for the default to come back up, or enter a custom one.");
			    Environment.Exit(4);
			}
			
			char[] split = { '|' };
			string[] mirrors = listSource.Replace("\n", "").Replace("\r", "").Split(split);
			MainClass.ConfigureOptions.RemoteHosts = mirrors;
			WebActions.Mirrors = mirrors;
			WebActions.PickRandomMirror();
			
			// Only use Tor if we haven't already setup a proxy.
			if (!MainClass.ConfigureOptions.Proxy.Enabled)
			{
    			Console.Write("Would you like to use Tor (must be setup with Privoxy)? [no] ");
    			string tor = Console.ReadLine().ToLower();
    			
    			if (tor.StartsWith("y"))
    			{
    			    MainClass.ConfigureOptions.Proxy.Hostname = "127.0.0.1";
    			    MainClass.ConfigureOptions.Proxy.Port = 8118;
    			    MainClass.ConfigureOptions.Proxy.Enabled = true;
    			    MainClass.ConfigureOptions.Proxy.Tor = true;
    			}
    		}
			
			// OK, now, ask about venues and regions (remember, r before v!)
			Console.WriteLine();
			Console.WriteLine(DumpRegions(false));
			Console.Write("Please enter your free-to-air region code: ");
			string regioncode = Console.ReadLine();
			MainClass.ConfigureOptions.Region.FreeToAir = regioncode;
			Console.Write("Do you want to download your cable listings, too? [no] ");
			string cabletoo = Console.ReadLine().ToLower();
			if (cabletoo == "y" || cabletoo == "yes")
			{
				Console.WriteLine();
				Console.WriteLine(DumpRegions(true));
				Console.Write("Please enter your cable provider code: ");
				string cablecode = Console.ReadLine();
				MainClass.ConfigureOptions.Region.Cable = cablecode;
			}
			
			Console.Write("Fetching channels, please be patient...");
			Dictionary<string, string> d = DumpChannels(MainClass.ConfigureOptions.Region.FreeToAir, MainClass.ConfigureOptions.Region.Cable);
			Console.WriteLine();
			
			Console.WriteLine();
			Console.WriteLine("For each channel you would like to download data for, enter an XMLID.");
			Console.WriteLine("Otherwise, leave it blank and hit Enter instead.");
			
			List<string> venues = new List<string>();
			Dictionary<string, string> channelmap = new Dictionary<string,string>();
			
			foreach(KeyValuePair<string, string> kv in d)
			{
				Console.Write("{0}: ", kv.Value.PadRight(Console.BufferWidth / 2));
				string xmlid = Console.ReadLine();
				if (xmlid.Length > 0)
				{
					venues.Add(kv.Key);
					channelmap.Add(xmlid, kv.Value);
				}
			}
			
			string[] venuelist = new string[venues.Count];
			int x = 0;
			foreach(string s in venues)
			{
				venuelist[x] = s;
				x++;
			}
			
			MainClass.ConfigureOptions.VenueIDList = venuelist;
			
			foreach(KeyValuePair<string, string> kv in channelmap)
			{
				tvgrabau.GrabberOptions.ChannelMap c = new tvgrabau.GrabberOptions.ChannelMap();
				c.Name = kv.Value;
				c.XMLID = kv.Key;
				MainClass.ConfigureOptions.ChannelMaps.Add(c);
			}
			
			// Legal crap. :(
			Console.WriteLine();
			Console.WriteLine("Do you agree to only use the resulting data, as well as any guide data");
			Console.Write("contained on any remote server for personal, non-commerical use? (Y/n) ");
			string PleaseDontSue = Console.ReadLine();
			if (!PleaseDontSue.ToLower().StartsWith("y"))
			{   
			    Console.WriteLine("Disabling descriptions on programme data.");
			    MainClass.ConfigureOptions.NoDescriptions = true;
			}
						
			Console.WriteLine();
			Console.WriteLine("Completed configuration!");
		}
		
		private static Dictionary<string, string> DumpChannels(string ftaid, string cableid)
		{
			WebManager.GetPage("http://www.yourtv.com.au/");
			Console.Write(".");
			WebManager.GetPage("http://www.yourtv.com.au/guide/index.cfm?cookies=1");
			Console.Write(".");
			string fta = WebManager.GetPage("http://www.yourtv.com.au/profile/ajax.cfm?action=channels&region_id="+ftaid+"&showsub=0");
			Console.Write(".");
			if (cableid != "0")
			{
				fta += WebManager.GetPage("http://www.yourtv.com.au/profile/ajax.cfm?action=channels&region_id="+cableid+"&showsub=0");
				Console.Write(".");
			}
			
			Dictionary<string, string> fd = new Dictionary<string, string>();
			
			//Console.WriteLine(fta);
			
			try
			{
				bool done = false;
				int offset = 0;
				while (!done)
				{
					string sstr = "<label for=\"venue_id_";
					int nextstart = fta.IndexOf(sstr, offset);
					if (nextstart > -1)
					{
						nextstart += sstr.Length;
						int end = fta.IndexOf("</label>", nextstart);
						string usplit = fta.Substring(nextstart, end - nextstart);
						char[] splito = { '_' };
						string[] splity = usplit.Split(splito);
						// Only use second half (important part)
						int endid = splity[1].IndexOf("\"");
						Console.Write(".");
						offset = nextstart;
						
						
						// Now, work out name!
						string name = splity[1].Substring(endid + 2).Replace("\n", "").Trim();
						string id = splity[1].Substring(0, endid);
						if (!fd.ContainsKey(id))
						{
							//Console.WriteLine("ID: {0}, Name: {1}", id, name);
							fd.Add(id, name);
						}
					}
					else
					{
						done = true;
					}
				}
			}
			catch (Exception)
			{
				// Fail silently.
			}
			
			return fd;
		}
		
		private static string DumpRegions(bool cable)
		{
			string extra = "";
			if (cable)
			{
				extra = "-cable";
			}
			string regions = WebManager.GetPage(WebActions.Mirrors[WebActions.MirrorIndex] + "regions" + extra, false);
		
			StringWriter tw = new StringWriter();
			char[] newline = { '\n' };
			char[] linesplit = { '\t' };
			
			string[] nlsplit = regions.Replace("\r", "").Split(newline);
			foreach (string l in nlsplit)
			{
				if (l.Length > 1)
				{
					string[] sl = l.Split(linesplit);
					tw.WriteLine("{0}\t= {1}", sl[0], sl[1]);
				}
			}
			
			return tw.ToString();
		}
		
		public static void DrawProgress()
		{
			if (MainClass.loud)
			{
				Console.Write("\r");
				int PadLen = Width / 2;
				string WLabel = Label;
				
				// TODO: Better calc.
				
				if (PadLen < 0)
				{
					WLabel = WLabel.Substring(0, Math.Abs(PadLen));
					WLabel += "...";
				}
				
				Console.Write(WLabel.PadRight(PadLen) + " " + Utils.PrintPercent(Math.Ceiling(Percent), Width).PadLeft(Width));
			}
		}
		
		private static void PerformGrab()
		{
			dt = DateTime.Now;
			dt = dt.AddDays(MainClass.ar.DayOffset);
			int DaysLeft = MainClass.ar.Days;
			//ArrayList PListArrayList = new ArrayList();
				
			while (DaysLeft > 0)
			{
				Label = "Grabbing " + dt.ToShortDateString() + "...";
				YourTV.Percent = 0;
				YourTV.DrawProgress();
				List<Programme>[] pl = Generator.ProcessDay(dt);
				Label = "Writing to memory...";
				YourTV.Percent = 100;
				YourTV.DrawProgress();
				Writer(pl);
				
				Label = "Communicating with server...";
				YourTV.DrawProgress();
				DaysLeft--;
				dt = dt.AddDays(1);
				YourTV.CookieContainer = new CookieContainer();
				Setup.SetRegion(tvgrabau.MainClass.options.Region.FreeToAir, tvgrabau.MainClass.options.Region.Cable, tvgrabau.MainClass.options.VenueIDList);
			}
			
			Label = "Finishing up...";
			YourTV.DrawProgress();
			
			// Set the List up..
			MainClass.p = SpitzeListe;
		}
		
		public static bool Writer(List<Programme>[] pl)
		{
			bool OK = true;
		
			try
			{
				foreach (List<Programme> p in pl)
				{
					foreach (Programme f in p)
					{
						// Add it!
						SpitzeListe.Add(f);
					}
				}
			}
			catch (System.Exception)
			{
				OK = false;
			}
			
			/*Console.WriteLine("Total programmes: {0}", SpitzeListe.Count);
			string r = "OK";
			if (!OK)
			{
				r = "Error";
			}
			Console.WriteLine("Last programme collection: returned {0}", r);*/
			
			return OK;
		}
		
		public static int findNextChar(char inChar, string inString, int startIdx)
	    {
	    	int stringPos = startIdx;
	        while (inChar != inString[stringPos])
	        {
	            stringPos++;
	        }
		    return stringPos;
	    }
	    
	    /// <summary>
	    /// Prints a brief user command listing to the screen.
	    /// </summary>
		public static void PrintHelp()
		{
	        Console.WriteLine("Generates XMLTV listing files for Australian television, based on the\nyourtv.com.au data.");
	        Console.WriteLine("Usage: tv_grab_au [arguments]");
	        Console.WriteLine();
	        Console.WriteLine("Possbile Arguments:");
	        Console.WriteLine("--configure");
	        Console.WriteLine("\tRun's the application in configuration mode.");
	        Console.WriteLine();
	        Console.WriteLine("--config <filename>");
	        Console.WriteLine("\tUses the specified config file rather than the default.");
	        Console.WriteLine("--channelmap <filename>");
	        Console.WriteLine("\tUses the specified channelmap, rather than the one specified in the\n\tconfig file.");
	        Console.WriteLine("--output <filename>");
	        Console.WriteLine("\tOutputs the resulting XML guide into the specified file.");
	        Console.WriteLine("--days <days>");
	        Console.WriteLine("\tNumber of days to grab. (overrides value in config)");
	        Console.WriteLine("--offset <days>");
	        Console.WriteLine("\tAmount of days to offset grab date (negative values can be used).");
	        Console.WriteLine("--today");
	        Console.WriteLine("\tFetches only the data from the current time until the rest of the\n\tday (bandwidth saver).");
	        Console.WriteLine("--quiet");
	        Console.WriteLine("\tRuns the application in quiet mode (generates no output - suppresses\n\terrors).");
	        Console.WriteLine("--fast");
	        Console.WriteLine("\tOverrides the fetch delay between programmes to zero - not recommended.");
	        Console.WriteLine("--list-channels");
	        Console.WriteLine("\tLists the channels associated with the current config, or as set by\n\tthe --ftid and --cableid arguments.");
	        Console.WriteLine("--ftid <areaid>");
	        Console.WriteLine("\tSets the current Free-to-Air area ID to the one provided, rather than\n\tthe one written in the config file.");
	        Console.WriteLine("--cableid <providerid>");
	        Console.WriteLine("\tSets the current Cable provider ID to the one provided, rather than\n\tthe one written in the config file");
	        Console.WriteLine("--version");
	        Console.WriteLine("\tPrints versioning information.");
	        Console.WriteLine("--help");
	        Console.WriteLine("\tThis message. :)");
	    	Console.WriteLine();
	    	//MainClass.ReturnCode = 0;
		}
		
		/// <summary>
		/// Returns the --description value for the grabber, so that the grabber can be found by <c>tv_find_grabbers</c>.
		/// </summary>
		public static void Description()
		{
			Console.WriteLine("Australia (YourTV)");
			//MainClass.ReturnCode = 0;
		}
		
		/// <summary>
		/// Returns the --capabilities value for the grabber, so that the grabber can be found by <c>tv_find_grabbers</c>.
		/// </summary>
		public static void Capabilities()
		{
			// Return XMLTV capabilities
			Console.WriteLine("baseline");
			Console.WriteLine("manualconfig");
			Console.WriteLine("cache");
			//MainClass.ReturnCode = 0;
		}
	}
}
